﻿using Microsoft.Win32;
using Newtonsoft.Json;
using System;
using System.Drawing;
using System.IO;
using System.Net.WebSockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ClipSyncWindows
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ClipboardSyncService());
        }
    }

    public class ClipboardSyncService : Form
    {
        private NotifyIcon notifyIcon;
        private ContextMenuStrip contextMenu;
        private ClientWebSocket webSocket;
        private CancellationTokenSource cts;
        private System.Threading.Timer reconnectTimer;

        private string _lastClipboardText = "";
        private DateTime _lastWriteTime = DateTime.MinValue;
        private const int MIN_INTERVAL_MS = 200; // 防止短时间内重复同步

        // 保存连接地址
        private string _wsAddress;
        private const string ConfigFile = "config.json";

        [DllImport("user32.dll")]
        private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);

        [DllImport("user32.dll")]
        private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll")]
        private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        private const int WM_DRAWCLIPBOARD = 0x0308;
        private const int WM_CHANGECBCHAIN = 0x030D;
        private IntPtr nextClipboardViewer;

        // 开机自启菜单项
        private ToolStripMenuItem autoStartMenuItem;

        public ClipboardSyncService()
        {
            // 隐藏主窗口
            this.WindowState = FormWindowState.Minimized;
            this.ShowInTaskbar = false;
            this.FormBorderStyle = FormBorderStyle.None;

            // 加载配置
            LoadConfig();

            // 托盘菜单
            contextMenu = new ContextMenuStrip();
            contextMenu.Items.Add("退出", null, Exit_Click);
            contextMenu.Items.Add("连接", null, Connect_Click);
            contextMenu.Items.Add("断开", null, Disconnect_Click);
            contextMenu.Items.Add("设置连接地址", null, SetAddress_Click);

            //开机自启菜单项
            autoStartMenuItem = new ToolStripMenuItem("开机自启", null, AutoStart_Click);
            contextMenu.Items.Add(autoStartMenuItem);

            notifyIcon = new NotifyIcon();
            notifyIcon.Icon = SystemIcons.Application;
            notifyIcon.Text = "剪切板同步服务";
            notifyIcon.ContextMenuStrip = contextMenu;
            notifyIcon.Visible = true;

            // 初始化开机自启菜单项文本
            UpdateAutoStartMenuItemText();

            // 注册剪贴板监听
            nextClipboardViewer = SetClipboardViewer(this.Handle);

            // 断线重连定时器
            reconnectTimer = new System.Threading.Timer(OnReconnectTimerTick, null, Timeout.Infinite, Timeout.Infinite);

            // 初始连接
            ConnectWebSocket();
        }

        private void UpdateAutoStartMenuItemText()
        {
            autoStartMenuItem.Text = IsAutoStartEnabled() ? "取消开机自启" : "开机自启";
        }

        // 开机自启点击事件
        private void AutoStart_Click(object sender, EventArgs e)
        {
            bool isEnabled = IsAutoStartEnabled();

            if (isEnabled)
            {
                SetAutoStart(false);
                notifyIcon.ShowBalloonTip(2000, "已关闭", "开机自启已关闭", ToolTipIcon.Info);
            }
            else
            {
                SetAutoStart(true);
                notifyIcon.ShowBalloonTip(2000, "已开启", "开机自启已开启", ToolTipIcon.Info);
            }

            UpdateAutoStartMenuItemText();
        }

        // 加载配置
        private void LoadConfig()
        {
            try
            {
                if (File.Exists(ConfigFile))
                {
                    var json = File.ReadAllText(ConfigFile);
                    var config = JsonConvert.DeserializeObject<Config>(json);
                    _wsAddress = config.WsAddress;
                }
                else
                {
                    // 默认地址
                    _wsAddress = "ws://192.168.1.110:7096";
                    SaveConfig();
                }
            }
            catch
            {
                _wsAddress = "ws://192.168.1.110:7096";
                SaveConfig();
            }
        }

        // 保存配置
        private void SaveConfig()
        {
            try
            {
                var config = new Config { WsAddress = _wsAddress };
                var json = JsonConvert.SerializeObject(config, Formatting.Indented);
                File.WriteAllText(ConfigFile, json);
            }
            catch { }
        }

        // 设置连接地址
        private void SetAddress_Click(object sender, EventArgs e)
        {
            var inputForm = new InputForm("设置 WebSocket 连接地址", "请输入服务器地址：", _wsAddress);
            if (inputForm.ShowDialog() == DialogResult.OK)
            {
                _wsAddress = inputForm.InputText;
                SaveConfig();

                // 断开现有连接并重新连接
                DisconnectWebSocket();
                ConnectWebSocket();
            }
        }

        private async void ConnectWebSocket()
        {
            try
            {
                webSocket = new ClientWebSocket();
                cts = new CancellationTokenSource();
                await webSocket.ConnectAsync(new Uri(_wsAddress), cts.Token);
                notifyIcon.ShowBalloonTip(2000, "连接成功", $"已连接到 {_wsAddress}", ToolTipIcon.Info);
                StartListening();
                reconnectTimer.Change(Timeout.Infinite, Timeout.Infinite); // 连接成功则停止重连定时器
            }
            catch (Exception ex)
            {
                notifyIcon.ShowBalloonTip(2000, "连接失败", ex.Message, ToolTipIcon.Error);
                StartReconnectTimer();
            }
        }

        private void StartReconnectTimer()
        {
            reconnectTimer.Change(5000, 5000);
        }

        private void OnReconnectTimerTick(object state)
        {
            if (webSocket?.State != WebSocketState.Open)
            {
                ConnectWebSocket();
            }
        }

        private async void StartListening()
        {
            var buffer = new byte[1024 * 4];
            while (webSocket.State == WebSocketState.Open)
            {
                try
                {
                    var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), cts.Token);
                    if (result.MessageType == WebSocketMessageType.Text)
                    {
                        string message = Encoding.UTF8.GetString(buffer, 0, result.Count);
                        HandleWebSocketMessage(message);
                    }
                    else if (result.MessageType == WebSocketMessageType.Close)
                    {
                        await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", cts.Token);
                        notifyIcon.ShowBalloonTip(2000, "连接关闭", "服务器主动关闭连接", ToolTipIcon.Warning);
                        StartReconnectTimer();
                    }
                }
                catch
                {
                    notifyIcon.ShowBalloonTip(2000, "连接异常", "与服务器断开连接", ToolTipIcon.Error);
                    StartReconnectTimer();
                    break;
                }
            }
        }

        private void HandleWebSocketMessage(string message)
        {
            try
            {
                // 消息格式: {"text":"666"}
                dynamic data = JsonConvert.DeserializeObject(message);
                string text = data.text;

                // 防止循环同步
                if (text == _lastClipboardText ||
                    (DateTime.Now - _lastWriteTime).TotalMilliseconds < MIN_INTERVAL_MS)
                {
                    return;
                }

                // 写入剪贴板
                _lastClipboardText = text;
                _lastWriteTime = DateTime.Now;
                Clipboard.SetText(text);
            }
            catch { }
        }

        private async void SendClipboardToServer(string text)
        {
            if (webSocket?.State != WebSocketState.Open) return;

            try
            {
                var message = JsonConvert.SerializeObject(new
                {
                    text = text
                });
                var buffer = Encoding.UTF8.GetBytes(message);
                await webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Text, true, cts.Token);
            }
            catch { }
        }

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_DRAWCLIPBOARD:
                    OnClipboardChange();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }

        private void OnClipboardChange()
        {
            try
            {
                IDataObject iData = Clipboard.GetDataObject();
                if (iData.GetDataPresent(DataFormats.Text))
                {
                    string text = (string)iData.GetData(DataFormats.Text);

                    // 防止循环同步
                    if (text == _lastClipboardText ||
                        (DateTime.Now - _lastWriteTime).TotalMilliseconds < MIN_INTERVAL_MS)
                    {
                        return;
                    }

                    _lastClipboardText = text;
                    _lastWriteTime = DateTime.Now;
                    SendClipboardToServer(text);
                }
            }
            catch { }
        }

        // 检查是否已设置开机自启
        private bool IsAutoStartEnabled()
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run", false))
            {
                return key.GetValue("ClipboardSync") != null;
            }
        }

        // 设置开机自启
        private void SetAutoStart(bool enable)
        {
            using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run", true))
            {
                if (enable)
                    key.SetValue("ClipboardSync", Application.ExecutablePath);
                else
                    key.DeleteValue("ClipboardSync", false);
            }
        }

        private void Exit_Click(object sender, EventArgs e)
        {
            DisconnectWebSocket();
            ChangeClipboardChain(this.Handle, nextClipboardViewer);
            notifyIcon.Dispose();
            Application.Exit();
        }

        private void Connect_Click(object sender, EventArgs e)
        {
            ConnectWebSocket();
        }

        private void Disconnect_Click(object sender, EventArgs e)
        {
            DisconnectWebSocket();
            StartReconnectTimer();
        }

        private async void DisconnectWebSocket()
        {
            if (webSocket?.State == WebSocketState.Open)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
            }
            cts?.Cancel();
            notifyIcon.ShowBalloonTip(2000, "已断开", "已与服务器断开连接", ToolTipIcon.Info);
        }
    }

    // 配置类
    public class Config
    {
        public string WsAddress { get; set; }
    }

    // 输入框窗体
    public class InputForm : Form
    {
        private TextBox inputTextBox;
        private Button okButton;
        private Button cancelButton;

        public string InputText => inputTextBox.Text;

        public InputForm(string title, string labelText, string defaultValue)
        {
            this.Text = title;
            this.Size = new Size(300, 150);
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedDialog;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.AcceptButton = okButton;
            this.CancelButton = cancelButton;

            var label = new Label
            {
                Text = labelText,
                Location = new Point(20, 20),
                AutoSize = true
            };

            inputTextBox = new TextBox
            {
                Location = new Point(20, 50),
                Width = 240,
                Text = defaultValue
            };

            okButton = new Button
            {
                Text = "确定",
                Location = new Point(60, 90),
                DialogResult = DialogResult.OK
            };

            cancelButton = new Button
            {
                Text = "取消",
                Location = new Point(160, 90),
                DialogResult = DialogResult.Cancel
            };

            this.Controls.Add(label);
            this.Controls.Add(inputTextBox);
            this.Controls.Add(okButton);
            this.Controls.Add(cancelButton);
        }
    }
}